﻿#include "SerialPort.h"
#include <iostream>
#include <cstring>

NahidaProject::SerialPort::SerialPort() : isOpen_(false), isAsyncReading_(false), baudRate_(BaudRate::BAUDRATE_9600), dataBits_(DataBits::DATA_8), stopBits_(StopBits::STOP_1), parity_(Parity::NONE), flowControl_(FlowControl::NONE) {
    
#ifdef _WIN32
    handle_ = INVALID_HANDLE_VALUE;
    memset(&overlapped_, 0, sizeof(OVERLAPPED));
#else
    fd_ = -1;
#endif
}

NahidaProject::SerialPort::~SerialPort() {
    StopAsyncRead();
    CloseSerialPort();
}

NahidaProject::SerialPort::ErrorCode NahidaProject::SerialPort::OpenSerialPort(const std::string& portName) {
    if (isOpen_) {
        return ErrorCode::PORT_ALREADY_OPEN;
    }

    portName_ = portName;

#ifdef _WIN32
    // Windows 实现
    std::string fullPortName = "\\\\.\\" + portName;
    handle_ = CreateFileA(fullPortName.c_str(),GENERIC_READ | GENERIC_WRITE,0,NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);

    if (handle_ == INVALID_HANDLE_VALUE) {
        return ErrorCode::OPEN_FAILED;
    }

    // 初始化重叠结构
    memset(&overlapped_, 0, sizeof(OVERLAPPED));
    overlapped_.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (overlapped_.hEvent == NULL) {
        CloseHandle(handle_);
        return ErrorCode::OPEN_FAILED;
    }

#else
    // Linux 实现
    fd_ = ::open(portName.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
    if (fd_ == -1) {
        return ErrorCode::OPEN_FAILED;
    }

    // 设置为阻塞模式
    int flags = fcntl(fd_, F_GETFL, 0);
    fcntl(fd_, F_SETFL, flags & ~O_NONBLOCK);
#endif

    isOpen_ = true;
    return ErrorCode::SUCCESS;
}

void NahidaProject::SerialPort::CloseSerialPort() {
    if (!isOpen_) return;

    StopAsyncRead();

#ifdef _WIN32
    if (handle_ != INVALID_HANDLE_VALUE) {
        CloseHandle(handle_);
        handle_ = INVALID_HANDLE_VALUE;
    }
    if (overlapped_.hEvent != NULL) {
        CloseHandle(overlapped_.hEvent);
        overlapped_.hEvent = NULL;
    }
#else
    if (fd_ != -1) {
        ::close(fd_);
        fd_ = -1;
    }
#endif

    isOpen_ = false;
}

bool NahidaProject::SerialPort::SerialPortIsOpen() const {
    return isOpen_;
}

NahidaProject::SerialPort::ErrorCode NahidaProject::SerialPort::Configure(BaudRate baudRate, DataBits dataBits, StopBits stopBits, Parity parity, FlowControl flowControl) {
    
    if (!isOpen_) {
        return ErrorCode::PORT_NOT_OPEN;
    }

    baudRate_ = baudRate;
    dataBits_ = dataBits;
    stopBits_ = stopBits;
    parity_ = parity;
    flowControl_ = flowControl;

    return ConfigureSerialPort();
}

NahidaProject::SerialPort::ErrorCode NahidaProject::SerialPort::ConfigureSerialPort() {
#ifdef _WIN32
    DCB dcb;
    memset(&dcb, 0, sizeof(DCB));
    dcb.DCBlength = sizeof(DCB);

    if (!GetCommState(handle_, &dcb)) {
        return ErrorCode::CONFIGURE_FAILED;
    }

    // 设置波特率
    dcb.BaudRate = static_cast<DWORD>(baudRate_);

    // 设置数据位
    dcb.ByteSize = static_cast<BYTE>(dataBits_);

    // 设置停止位
    switch (stopBits_) {
        case StopBits::STOP_1: dcb.StopBits = ONESTOPBIT; break;
        case StopBits::STOP_1_5: dcb.StopBits = ONE5STOPBITS; break;
        case StopBits::STOP_2: dcb.StopBits = TWOSTOPBITS; break;
    }

    // 设置校验位
    switch (parity_) {
        case Parity::NONE:
            dcb.Parity = NOPARITY;
            dcb.fParity = FALSE;
            break;
        case Parity::ODD:
            dcb.Parity = ODDPARITY;
            dcb.fParity = TRUE;
            break;
        case Parity::EVEN:
            dcb.Parity = EVENPARITY;
            dcb.fParity = TRUE;
            break;
        case Parity::MARK:
            dcb.Parity = MARKPARITY;
            dcb.fParity = TRUE;
            break;
        case Parity::SPACE:
            dcb.Parity = SPACEPARITY;
            dcb.fParity = TRUE;
            break;
    }

    // 设置流控制
    switch (flowControl_) {
        case FlowControl::NONE:
            dcb.fOutxCtsFlow = FALSE;
            dcb.fRtsControl = RTS_CONTROL_DISABLE;
            dcb.fInX = FALSE;
            dcb.fOutX = FALSE;
            break;
        case FlowControl::HARDWARE:
            dcb.fOutxCtsFlow = TRUE;
            dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
            dcb.fInX = FALSE;
            dcb.fOutX = FALSE;
            break;
        case FlowControl::SOFTWARE:
            dcb.fOutxCtsFlow = FALSE;
            dcb.fRtsControl = RTS_CONTROL_DISABLE;
            dcb.fInX = TRUE;
            dcb.fOutX = TRUE;
            break;
    }

    if (!SetCommState(handle_, &dcb)) {
        return ErrorCode::CONFIGURE_FAILED;
    }

    // 设置超时
    COMMTIMEOUTS timeouts;
    timeouts.ReadIntervalTimeout = MAXDWORD;
    timeouts.ReadTotalTimeoutMultiplier = 0;
    timeouts.ReadTotalTimeoutConstant = 0;
    timeouts.WriteTotalTimeoutMultiplier = 0;
    timeouts.WriteTotalTimeoutConstant = 0;

    if (!SetCommTimeouts(handle_, &timeouts)) {
        return ErrorCode::CONFIGURE_FAILED;
    }

#else
    struct termios options;
    if (tcgetattr(fd_, &options) == -1) {
        return ErrorCode::CONFIGURE_FAILED;
    }

    // 设置波特率
    speed_t baud = B9600;
    switch (baudRate_) {
        case BaudRate::BAUDRATE_110: baud = B110; break;
        case BaudRate::BAUDRATE_300: baud = B300; break;
        case BaudRate::BAUDRATE_600: baud = B600; break;
        case BaudRate::BAUDRATE_1200: baud = B1200; break;
        case BaudRate::BAUDRATE_2400: baud = B2400; break;
        case BaudRate::BAUDRATE_4800: baud = B4800; break;
        case BaudRate::BAUDRATE_9600: baud = B9600; break;
        case BaudRate::BAUDRATE_19200: baud = B19200; break;
        case BaudRate::BAUDRATE_38400: baud = B38400; break;
        case BaudRate::BAUDRATE_57600: baud = B57600; break;
        case BaudRate::BAUDRATE_115200: baud = B115200; break;
#ifdef B230400
        case BaudRate::BAUDRATE_230400: baud = B230400; break;
#endif
#ifdef B460800
        case BaudRate::BAUDRATE_460800: baud = B460800; break;
#endif
#ifdef B921600
        case BaudRate::BAUDRATE_921600: baud = B921600; break;
#endif
        default: baud = B9600; break;
    }
    cfsetispeed(&options, baud);
    cfsetospeed(&options, baud);

    // 设置字符大小
    options.c_cflag &= ~CSIZE;
    switch (dataBits_) {
        case DataBits::DATA_5: options.c_cflag |= CS5; break;
        case DataBits::DATA_6: options.c_cflag |= CS6; break;
        case DataBits::DATA_7: options.c_cflag |= CS7; break;
        case DataBits::DATA_8: options.c_cflag |= CS8; break;
    }

    // 设置停止位
    if (stopBits_ == StopBits::STOP_2) {
        options.c_cflag |= CSTOPB;
    } else {
        options.c_cflag &= ~CSTOPB;
    }

    // 设置校验位
    options.c_cflag &= ~(PARENB | PARODD);
    options.c_iflag &= ~(IGNPAR | PARMRK | INPCK);
    switch (parity_) {
        case Parity::NONE:
            options.c_cflag &= ~PARENB;
            break;
        case Parity::EVEN:
            options.c_cflag |= PARENB;
            options.c_cflag &= ~PARODD;
            options.c_iflag |= (IGNPAR | INPCK);
            break;
        case Parity::ODD:
            options.c_cflag |= (PARENB | PARODD);
            options.c_iflag |= (IGNPAR | INPCK);
            break;
        default:
            options.c_cflag &= ~PARENB;
            break;
    }

    // 设置流控制
    switch (flowControl_) {
        case FlowControl::NONE:
            options.c_cflag &= ~CRTSCTS;
            options.c_iflag &= ~(IXON | IXOFF | IXANY);
            break;
        case FlowControl::HARDWARE:
            options.c_cflag |= CRTSCTS;
            options.c_iflag &= ~(IXON | IXOFF | IXANY);
            break;
        case FlowControl::SOFTWARE:
            options.c_cflag &= ~CRTSCTS;
            options.c_iflag |= (IXON | IXOFF | IXANY);
            break;
    }

    // 设置原始模式
    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
    options.c_oflag &= ~OPOST;

    // 设置最小字符数和超时
    options.c_cc[VMIN] = 0;
    options.c_cc[VTIME] = 0;

    if (tcsetattr(fd_, TCSANOW, &options) == -1) {
        return ErrorCode::CONFIGURE_FAILED;
    }

    tcflush(fd_, TCIOFLUSH);
#endif

    return ErrorCode::SUCCESS;
}

NahidaProject::SerialPort::ErrorCode NahidaProject::SerialPort::Write(const std::vector<uint8_t>& data) {
    return Write(data.data(), data.size());
}

NahidaProject::SerialPort::ErrorCode NahidaProject::SerialPort::WriteString(const std::string& data) {
    return Write(reinterpret_cast<const uint8_t*>(data.c_str()), data.length());
}

NahidaProject::SerialPort::ErrorCode NahidaProject::SerialPort::Write(const uint8_t* data, size_t length) {
    if (!isOpen_) {
        return ErrorCode::PORT_NOT_OPEN;
    }

    if (data == nullptr || length == 0) {
        return ErrorCode::INVALID_PARAMETER;
    }

#ifdef _WIN32
    DWORD bytesWritten = 0;
    if (!WriteFile(handle_, data, static_cast<DWORD>(length), &bytesWritten, &overlapped_)) {
        if (GetLastError() != ERROR_IO_PENDING) {
            return ErrorCode::WRITE_FAILED;
        }
        
        if (!GetOverlappedResult(handle_, &overlapped_, &bytesWritten, TRUE)) {
            return ErrorCode::WRITE_FAILED;
        }
    }

    if (bytesWritten != length) {
        return ErrorCode::WRITE_FAILED;
    }
#else
    ssize_t result = ::write(fd_, data, length);
    if (result == -1 || static_cast<size_t>(result) != length) {
        return ErrorCode::WRITE_FAILED;
    }
#endif

    return ErrorCode::SUCCESS;
}

NahidaProject::SerialPort::ErrorCode NahidaProject::SerialPort::Read(std::vector<uint8_t>& data, size_t maxBytes,int timeoutMs, size_t* bytesRead) {
    if (!isOpen_) {
        return ErrorCode::PORT_NOT_OPEN;
    }

    if (maxBytes == 0) {
        if (bytesRead) *bytesRead = 0;
        return ErrorCode::SUCCESS;
    }

    data.resize(maxBytes);

#ifdef _WIN32
    DWORD dwBytesRead = 0;
    
    if (timeoutMs >= 0) {
        COMMTIMEOUTS timeouts;
        timeouts.ReadIntervalTimeout = MAXDWORD;
        timeouts.ReadTotalTimeoutMultiplier = 0;
        timeouts.ReadTotalTimeoutConstant = timeoutMs;
        timeouts.WriteTotalTimeoutMultiplier = 0;
        timeouts.WriteTotalTimeoutConstant = 0;
        SetCommTimeouts(handle_, &timeouts);
    }

    BOOL result = ReadFile(handle_, data.data(), static_cast<DWORD>(maxBytes), &dwBytesRead, &overlapped_);
    
    if (!result) {
        if (GetLastError() == ERROR_IO_PENDING) {
            if (!GetOverlappedResult(handle_, &overlapped_, &dwBytesRead, TRUE)) {
                if (GetLastError() == ERROR_TIMEOUT) {
                    return ErrorCode::TIMEOUT;
                }
                return ErrorCode::READ_FAILED;
            }
        } else {
            return ErrorCode::READ_FAILED;
        }
    }

    if (bytesRead) *bytesRead = static_cast<size_t>(dwBytesRead);
    data.resize(dwBytesRead);

#else
    if (timeoutMs >= 0) {
        fd_set readfds;
        FD_ZERO(&readfds);
        FD_SET(fd_, &readfds);

        struct timeval tv;
        tv.tv_sec = timeoutMs / 1000;
        tv.tv_usec = (timeoutMs % 1000) * 1000;

        int result = select(fd_ + 1, &readfds, NULL, NULL, &tv);
        if (result == 0) {
            if (bytesRead) *bytesRead = 0;
            data.clear();
            return ErrorCode::TIMEOUT;
        } else if (result < 0) {
            return ErrorCode::READ_FAILED;
        }
    }

    ssize_t result = ::read(fd_, data.data(), maxBytes);
    if (result < 0) {
        return ErrorCode::READ_FAILED;
    }

    if (bytesRead) *bytesRead = static_cast<size_t>(result);
    data.resize(result);
#endif

    return ErrorCode::SUCCESS;
}

NahidaProject::SerialPort::ErrorCode NahidaProject::SerialPort::ReadExact(std::vector<uint8_t>& data, size_t bytesToRead, int timeoutMs) {
    if (!isOpen_) {
        return ErrorCode::PORT_NOT_OPEN;
    }

    if (bytesToRead == 0) {
        data.clear();
        return ErrorCode::SUCCESS;
    }

    data.resize(bytesToRead);
    size_t totalBytesRead = 0;
    auto startTime = std::chrono::steady_clock::now();

    while (totalBytesRead < bytesToRead) {
        if (timeoutMs >= 0) {
            auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
                std::chrono::steady_clock::now() - startTime).count();
            if (elapsed >= timeoutMs) {
                data.resize(totalBytesRead);
                return ErrorCode::TIMEOUT;
            }
            
            int remainingTimeout = timeoutMs - static_cast<int>(elapsed);
            if (remainingTimeout <= 0) {
                data.resize(totalBytesRead);
                return ErrorCode::TIMEOUT;
            }

            std::vector<uint8_t> buffer;
            size_t bytesRead = 0;
            ErrorCode result = Read(buffer, bytesToRead - totalBytesRead, remainingTimeout, &bytesRead);
            
            if (result != ErrorCode::SUCCESS) {
                data.resize(totalBytesRead);
                return result;
            }

            if (bytesRead > 0) {
                memcpy(data.data() + totalBytesRead, buffer.data(), bytesRead);
                totalBytesRead += bytesRead;
            }
        } else {
            std::vector<uint8_t> buffer;
            size_t bytesRead = 0;
            ErrorCode result = Read(buffer, bytesToRead - totalBytesRead, -1, &bytesRead);
            
            if (result != ErrorCode::SUCCESS) {
                data.resize(totalBytesRead);
                return result;
            }

            if (bytesRead > 0) {
                memcpy(data.data() + totalBytesRead, buffer.data(), bytesRead);
                totalBytesRead += bytesRead;
            }
        }
    }

    return ErrorCode::SUCCESS;
}

void NahidaProject::SerialPort::SetDataReceivedCallback(const DataReceivedCallback& callback) {
    std::lock_guard<std::mutex> lock(callbackMutex_);
    dataReceivedCallback_ = callback;
}

void NahidaProject::SerialPort::SetErrorCallback(const ErrorCallback& callback) {
    std::lock_guard<std::mutex> lock(callbackMutex_);
    errorCallback_ = callback;
}

NahidaProject::SerialPort::ErrorCode NahidaProject::SerialPort::StartAsyncRead() {
    if (!isOpen_) {
        return ErrorCode::PORT_NOT_OPEN;
    }

    if (isAsyncReading_) {
        return ErrorCode::SUCCESS; // 已经在运行
    }

    isAsyncReading_ = true;
    asyncReadThread_ = std::thread(&SerialPort::AsyncReadLoop, this);
    return ErrorCode::SUCCESS;
}

void NahidaProject::SerialPort::StopAsyncRead() {
    if (!isAsyncReading_) return;

    isAsyncReading_ = false;
    
    if (asyncReadThread_.joinable()) {
        asyncReadThread_.join();
    }
}

void NahidaProject::SerialPort::AsyncReadLoop() {
    std::vector<uint8_t> buffer(1024);
    
    while (isAsyncReading_ && isOpen_) {
        std::vector<uint8_t> data;
        size_t bytesRead = 0;
        
        // 使用较短的超时时间以响应停止请求
        ErrorCode result = Read(data, buffer.size(), 100, &bytesRead);
        
        if (result == ErrorCode::SUCCESS && bytesRead > 0) {
            std::lock_guard<std::mutex> lock(callbackMutex_);
            if (dataReceivedCallback_) {
                dataReceivedCallback_(data);
            }
        } else if (result != ErrorCode::TIMEOUT && result != ErrorCode::SUCCESS) {
            std::lock_guard<std::mutex> lock(callbackMutex_);
            if (errorCallback_) {
                errorCallback_(result);
            }
            break;
        }
    }
}

NahidaProject::SerialPort::ErrorCode NahidaProject::SerialPort::FlushInput() {
    if (!isOpen_) {
        return ErrorCode::PORT_NOT_OPEN;
    }

#ifdef _WIN32
    if (!PurgeComm(handle_, PURGE_RXCLEAR)) {
        return ErrorCode::UNKNOWN_ERROR;
    }
#else
    if (tcflush(fd_, TCIFLUSH) == -1) {
        return ErrorCode::UNKNOWN_ERROR;
    }
#endif

    return ErrorCode::SUCCESS;
}

NahidaProject::SerialPort::ErrorCode NahidaProject::SerialPort::FlushOutput() {
    if (!isOpen_) {
        return ErrorCode::PORT_NOT_OPEN;
    }

#ifdef _WIN32
    if (!PurgeComm(handle_, PURGE_TXCLEAR)) {
        return ErrorCode::UNKNOWN_ERROR;
    }
#else
    if (tcflush(fd_, TCOFLUSH) == -1) {
        return ErrorCode::UNKNOWN_ERROR;
    }
#endif

    return ErrorCode::SUCCESS;
}

NahidaProject::SerialPort::ErrorCode NahidaProject::SerialPort::Flush() {
    if (!isOpen_) {
        return ErrorCode::PORT_NOT_OPEN;
    }

#ifdef _WIN32
    if (!PurgeComm(handle_, PURGE_RXCLEAR | PURGE_TXCLEAR)) {
        return ErrorCode::UNKNOWN_ERROR;
    }
#else
    if (tcflush(fd_, TCIOFLUSH) == -1) {
        return ErrorCode::UNKNOWN_ERROR;
    }
#endif

    return ErrorCode::SUCCESS;
}

bool NahidaProject::SerialPort::GetClearToSend() {
#ifdef _WIN32
    DWORD status;
    if (GetCommModemStatus(handle_, &status)) {
        return (status & MS_CTS_ON) != 0;
    }
#else
    int status;
    if (ioctl(fd_, TIOCMGET, &status) != -1) {
        return (status & TIOCM_CTS) != 0;
    }
#endif
    return false;
}

bool NahidaProject::SerialPort::GetDataSetReady() {
#ifdef _WIN32
    DWORD status;
    if (GetCommModemStatus(handle_, &status)) {
        return (status & MS_DSR_ON) != 0;
    }
#else
    int status;
    if (ioctl(fd_, TIOCMGET, &status) != -1) {
        return (status & TIOCM_DSR) != 0;
    }
#endif
    return false;
}

bool NahidaProject::SerialPort::GetRingIndicator() {
#ifdef _WIN32
    DWORD status;
    if (GetCommModemStatus(handle_, &status)) {
        return (status & MS_RING_ON) != 0;
    }
#else
    int status;
    if (ioctl(fd_, TIOCMGET, &status) != -1) {
        return (status & TIOCM_RI) != 0;
    }
#endif
    return false;
}

bool NahidaProject::SerialPort::GetCarrierDetect() {
#ifdef _WIN32
    DWORD status;
    if (GetCommModemStatus(handle_, &status)) {
        return (status & MS_RLSD_ON) != 0;
    }
#else
    int status;
    if (ioctl(fd_, TIOCMGET, &status) != -1) {
        return (status & TIOCM_CD) != 0;
    }
#endif
    return false;
}

NahidaProject::SerialPort::ErrorCode NahidaProject::SerialPort::SetRequestToSend(bool enable) {
    if (!isOpen_) {
        return ErrorCode::PORT_NOT_OPEN;
    }

#ifdef _WIN32
    if (!EscapeCommFunction(handle_, enable ? SETRTS : CLRRTS)) {
        return ErrorCode::UNKNOWN_ERROR;
    }
#else
    int status;
    if (ioctl(fd_, TIOCMGET, &status) == -1) {
        return ErrorCode::UNKNOWN_ERROR;
    }
    
    if (enable) {
        status |= TIOCM_RTS;
    } else {
        status &= ~TIOCM_RTS;
    }
    
    if (ioctl(fd_, TIOCMSET, &status) == -1) {
        return ErrorCode::UNKNOWN_ERROR;
    }
#endif

    return ErrorCode::SUCCESS;
}

NahidaProject::SerialPort::ErrorCode NahidaProject::SerialPort::SetDataTerminalReady(bool enable) {
    if (!isOpen_) {
        return ErrorCode::PORT_NOT_OPEN;
    }

#ifdef _WIN32
    if (!EscapeCommFunction(handle_, enable ? SETDTR : CLRDTR)) {
        return ErrorCode::UNKNOWN_ERROR;
    }
#else
    int status;
    if (ioctl(fd_, TIOCMGET, &status) == -1) {
        return ErrorCode::UNKNOWN_ERROR;
    }
    
    if (enable) {
        status |= TIOCM_DTR;
    } else {
        status &= ~TIOCM_DTR;
    }
    
    if (ioctl(fd_, TIOCMSET, &status) == -1) {
        return ErrorCode::UNKNOWN_ERROR;
    }
#endif

    return ErrorCode::SUCCESS;
}

std::string NahidaProject::SerialPort::GetErrorString(ErrorCode error) {
    switch (error) {
        case ErrorCode::SUCCESS: return "Success";
        case ErrorCode::INVALID_PARAMETER: return "Invalid parameter";
        case ErrorCode::PORT_NOT_OPEN: return "Port not open";
        case ErrorCode::PORT_ALREADY_OPEN: return "Port already open";
        case ErrorCode::OPEN_FAILED: return "Open failed";
        case ErrorCode::CONFIGURE_FAILED: return "Configure failed";
        case ErrorCode::WRITE_FAILED: return "Write failed";
        case ErrorCode::READ_FAILED: return "Read failed";
        case ErrorCode::TIMEOUT: return "Timeout";
        case ErrorCode::UNKNOWN_ERROR: return "Unknown error";
        default: return "Unknown error code";
    }
}
